#include "TextureManager.h"
#include "Renderer.h"
#include "matrix.h"

#include <assert.h>

#define RELEASE_COM_OBJECT(i) if(i!=NULL&& i) i->Release();

Renderer::Renderer()
{
    m_ScreenWidth = 0;
    m_ScreenHeight = 0;
    m_D3D = 0;
    m_Device = 0;
    m_Camera = 0;
}

Renderer::~Renderer()
{
    //dtor
}

void Renderer::Init (HWND hWnd, int screenWidth, int screenHeight)
{
    m_ScreenWidth = screenWidth;
    m_ScreenHeight = screenHeight;

    createRenderDevice (hWnd, screenWidth, screenHeight);
    setScreenMatrix (screenWidth, screenHeight);

    m_TextureManager.Init (m_Device);
}

void Renderer::Destroy ()
{
    RELEASE_COM_OBJECT(m_Device)
    RELEASE_COM_OBJECT(m_D3D)
}


void Renderer::createRenderDevice (HWND hWnd, int screenWidth, int screenHeight)
{
    m_D3D = Direct3DCreate9(D3D_SDK_VERSION);

    D3DPRESENT_PARAMETERS d3dpp;

    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

    d3dpp.hDeviceWindow = hWnd;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = screenWidth;
    d3dpp.BackBufferHeight = screenHeight;
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

    m_D3D->CreateDevice (D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &m_Device);
}

void Renderer::setScreenMatrix(int screen_width, int screen_height)
{
    D3DXMATRIX matProjection;
    D3DXMatrixPerspectiveFovLH (&matProjection, DEGTORAD(45.0), (FLOAT)screen_width / (FLOAT)screen_height, 0.01, 100.0);
    m_Device->SetTransform(D3DTS_PROJECTION, &matProjection);
}

void Renderer::BeginScene()
{
    m_Device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 200, 200), 1.0f, 0);
    m_Device->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 200, 200), 1.0f, 0);
    m_Device->BeginScene();
}

void Renderer::EndScene()
{
    m_Device->EndScene();
    m_Device->Present(NULL, NULL, NULL, NULL) ;
}

void Renderer::RegisterCamera (Camera *camera)
{
    m_Camera = camera;
}

void Renderer::UnregisterCamera ()
{
    m_Camera = 0;
}

void Renderer::PrepareSurface ()
{
    assert(m_Device);
    // Allow multiple passes to blend together correctly
	m_Device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	m_Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	m_Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

	m_Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	m_Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	m_Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

    // Prevent some ugliness with the alphamaps
	m_Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
	m_Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);

	m_Device->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	m_Device->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	m_Device->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
}

void Renderer::DrawSurfacePatch (terrain::Patch *patch)
{
    assert(m_Device);

    m_Device->SetFVF (D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEX2);

    // Alphamap: take the alpha from the alphamap, we don't care about the color
	m_Device->SetTextureStageState (0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
	m_Device->SetTextureStageState (0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);

	// Texture: take the color from the texture, take the alpha from the previous stage
	m_Device->SetTextureStageState (1, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
	m_Device->SetTextureStageState (1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
	m_Device->SetTextureStageState (1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
	m_Device->SetTextureStageState (1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);

	// Disable textures from the pixel shader mode
	m_Device->SetTextureStageState (2, D3DTSS_COLOROP, D3DTOP_DISABLE);
	m_Device->SetTextureStageState (2, D3DTSS_ALPHAOP, D3DTOP_DISABLE);

	// Disable the pixel shader
	m_Device->SetPixelShader (NULL);

    m_Device->SetRenderState(D3DRS_LIGHTING, FALSE);
	m_Device->SetRenderState(D3DRS_ZENABLE, TRUE);


	dxTerrainVertex vertexes[4];

	assert (patch);

	vertexes[0].x = patch->GetBotL()->GetPosition().X;
    vertexes[0].y = patch->GetBotL()->GetPosition().Y;
    vertexes[0].z = -patch->GetBotL()->GetPosition().Z;

    vertexes[1].x = patch->GetTopL()->GetPosition().X;
    vertexes[1].y = patch->GetTopL()->GetPosition().Y;
    vertexes[1].z = -patch->GetTopL()->GetPosition().Z;

    vertexes[2].x = patch->GetBotR()->GetPosition().X;
    vertexes[2].y = patch->GetBotR()->GetPosition().Y;
    vertexes[2].z = -patch->GetBotR()->GetPosition().Z;

    vertexes[3].x = patch->GetTopR()->GetPosition().X;
    vertexes[3].y = patch->GetTopR()->GetPosition().Y;
    vertexes[3].z = -patch->GetTopR()->GetPosition().Z;

    vertexes[0].u1 = 0;
    vertexes[0].v1 = 0;
    vertexes[1].u1 = 1;
    vertexes[1].v1 = 0;
    vertexes[2].u1 = 0;
    vertexes[2].v1 = 1;
    vertexes[3].u1 = 1;
    vertexes[3].v1 = 1;

    vertexes[0].u2 = 0;
    vertexes[0].v2 = 0;
    vertexes[1].u2 = 1;
    vertexes[1].v2 = 0;
    vertexes[2].u2 = 0;
    vertexes[2].v2 = 1;
    vertexes[3].u2 = 1;
    vertexes[3].v2 = 1;

    m_Device->SetTexture(0, m_TextureManager.GetAlphaFull());
	m_Device->SetTexture(1, m_TextureManager.GetSand());
	m_Device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, vertexes, sizeof(dxTerrainVertex));

    m_Device->SetTexture(0, m_TextureManager.GetAlphaHalfSide());
	m_Device->SetTexture(1, m_TextureManager.GetMud());
	m_Device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, vertexes, sizeof(dxTerrainVertex));
}

void Renderer::CameraTransformation (D3DXMATRIX viewMatrix)
{
    m_Device->SetTransform(D3DTS_VIEW, &viewMatrix);
}
